home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 1.iso / dist / fw_cvs.idb / usr / freeware / bin / rcs2log.z / rcs2log
Text File  |  2002-01-08  |  18KB  |  678 lines

  1. #! /bin/sh
  2.  
  3. # RCS to ChangeLog generator
  4.  
  5. # Generate a change log prefix from RCS files (perhaps in the CVS repository)
  6. # and the ChangeLog (if any).
  7. # Output the new prefix to standard output.
  8. # You can edit this prefix by hand, and then prepend it to ChangeLog.
  9.  
  10. # Ignore log entries that start with `#'.
  11. # Clump together log entries that start with `{topic} ',
  12. # where `topic' contains neither white space nor `}'.
  13.  
  14. Help='The default FILEs are the files registered under the working directory.
  15. Options:
  16.  
  17.   -c CHANGELOG  Output a change log prefix to CHANGELOG (default ChangeLog).
  18.   -h HOSTNAME  Use HOSTNAME in change log entries (default current host).
  19.   -i INDENT  Indent change log lines by INDENT spaces (default 8).
  20.   -l LENGTH  Try to limit log lines to LENGTH characters (default 79).
  21.   -R  If no FILEs are given and RCS is used, recurse through working directory.
  22.   -r OPTION  Pass OPTION to subsidiary log command.
  23.   -t TABWIDTH  Tab stops are every TABWIDTH characters (default 8).
  24.   -u "LOGIN<tab>FULLNAME<tab>MAILADDR"  Assume LOGIN has FULLNAME and MAILADDR.
  25.   -v  Append RCS revision to file names in log lines.
  26.   --help  Output help.
  27.   --version  Output version number.
  28.  
  29. Report bugs to <bug-gnu-emacs@gnu.org>.'
  30.  
  31. Id='$Id: rcs2log,v 1.45 1998/08/12 22:33:01 eggert Exp $'
  32.  
  33. # Copyright 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
  34.  
  35. # This program is free software; you can redistribute it and/or modify
  36. # it under the terms of the GNU General Public License as published by
  37. # the Free Software Foundation; either version 2, or (at your option)
  38. # any later version.
  39. #
  40. # This program is distributed in the hope that it will be useful,
  41. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  42. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  43. # GNU General Public License for more details.
  44. #
  45. # You should have received a copy of the GNU General Public License
  46. # along with this program; see the file COPYING.  If not, write to the
  47. # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  48. # Boston, MA 02111-1307, USA.
  49.  
  50. Copyright='Copyright 1998 Free Software Foundation, Inc.
  51. This program comes with NO WARRANTY, to the extent permitted by law.
  52. You may redistribute copies of this program
  53. under the terms of the GNU General Public License.
  54. For more information about these matters, see the files named COPYING.
  55. Author: Paul Eggert <eggert@twinsun.com>'
  56.  
  57. tab='    '
  58. nl='
  59. '
  60.  
  61. # Parse options.
  62.  
  63. # defaults
  64. : ${AWK=awk}
  65. : ${TMPDIR=/tmp}
  66. changelog=ChangeLog # change log file name
  67. datearg= # rlog date option
  68. hostname= # name of local host (if empty, will deduce it later)
  69. indent=8 # indent of log line
  70. length=79 # suggested max width of log line
  71. logins= # login names for people we know fullnames and mailaddrs of
  72. loginFullnameMailaddrs= # login<tab>fullname<tab>mailaddr triplets
  73. logTZ= # time zone for log dates (if empty, use local time)
  74. recursive= # t if we want recursive rlog
  75. revision= # t if we want revision numbers
  76. rlog_options= # options to pass to rlog
  77. tabwidth=8 # width of horizontal tab
  78.  
  79. while :
  80. do
  81.     case $1 in
  82.     -c)    changelog=${2?}; shift;;
  83.     -i)    indent=${2?}; shift;;
  84.     -h)    hostname=${2?}; shift;;
  85.     -l)    length=${2?}; shift;;
  86.     -[nu])    # -n is obsolescent; it is replaced by -u.
  87.         case $1 in
  88.         -n)    case ${2?}${3?}${4?} in
  89.             *"$tab"* | *"$nl"*)
  90.                 echo >&2 "$0: -n '$2' '$3' '$4': tabs, newlines not allowed"
  91.                 exit 1
  92.             esac
  93.             case $loginFullnameMailaddrs in
  94.             '') loginFullnameMailaddrs=$2$tab$3$tab$4;;
  95.             ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2$tab$3$tab$4
  96.             esac
  97.             shift; shift; shift;;
  98.         -u)
  99.             # If $2 is not tab-separated, use colon for separator.
  100.             case ${2?} in
  101.             *"$nl"*)
  102.                 echo >&2 "$0: -u '$2': newlines not allowed"
  103.                 exit 1;;
  104.             *"$tab"*)
  105.                 t=$tab;;
  106.             *)
  107.                 t=:
  108.             esac
  109.             case $2 in
  110.             *"$t"*"$t"*"$t"*)
  111.                 echo >&2 "$0: -u '$2': too many fields"
  112.                 exit 1;;
  113.             *"$t"*"$t"*)
  114.                 ;;
  115.             *)
  116.                 echo >&2 "$0: -u '$2': not enough fields"
  117.                 exit 1
  118.             esac
  119.             case $loginFullnameMailaddrs in
  120.             '') loginFullnameMailaddrs=$2;;
  121.             ?*) loginFullnameMailaddrs=$loginFullnameMailaddrs$nl$2
  122.             esac
  123.             shift
  124.         esac
  125.         case $logins in
  126.         '') logins=$login;;
  127.         ?*) logins=$logins$nl$login
  128.         esac
  129.         ;;
  130.     -r)
  131.         case $rlog_options in
  132.         '') rlog_options=${2?};;
  133.         ?*) rlog_options=$rlog_options$nl${2?}
  134.         esac
  135.         shift;;
  136.     -R)    recursive=t;;
  137.     -t)    tabwidth=${2?}; shift;;
  138.     -v)    revision=t;;
  139.     --version)
  140.         set $Id
  141.         rcs2logVersion=$3
  142.         echo >&2 "rcs2log (GNU Emacs) $rcs2logVersion$nl$Copyright"
  143.         exit 0;;
  144.     -*)    echo >&2 "Usage: $0 [OPTION]... [FILE ...]$nl$Help"
  145.         case $1 in
  146.         --help) exit 0;;
  147.         *) exit 1
  148.         esac;;
  149.     *)    break
  150.     esac
  151.     shift
  152. done
  153.  
  154. month_data='
  155.     m[0]="Jan"; m[1]="Feb"; m[2]="Mar"
  156.     m[3]="Apr"; m[4]="May"; m[5]="Jun"
  157.     m[6]="Jul"; m[7]="Aug"; m[8]="Sep"
  158.     m[9]="Oct"; m[10]="Nov"; m[11]="Dec"
  159. '
  160.  
  161.  
  162. # Put rlog output into $rlogout.
  163.  
  164. # If no rlog options are given,
  165. # log the revisions checked in since the first ChangeLog entry.
  166. # Since ChangeLog is only by date, some of these revisions may be duplicates of
  167. # what's already in ChangeLog; it's the user's responsibility to remove them.
  168. case $rlog_options in
  169. '')
  170.     if test -s "$changelog"
  171.     then
  172.         e='
  173.             /^[0-9]+-[0-9][0-9]-[0-9][0-9]/{
  174.                 # ISO 8601 date
  175.                 print $1
  176.                 exit
  177.             }
  178.             /^... ... [ 0-9][0-9] [ 0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9]+ /{
  179.                 # old-fashioned date and time (Emacs 19.31 and earlier)
  180.                 '"$month_data"'
  181.                 year = $5
  182.                 for (i=0; i<=11; i++) if (m[i] == $2) break
  183.                 dd = $3
  184.                 printf "%d-%02d-%02d\n", year, i+1, dd
  185.                 exit
  186.             }
  187.         '
  188.         d=`$AWK "$e" <"$changelog"` || exit
  189.         case $d in
  190.         ?*) datearg="-d>$d"
  191.         esac
  192.     fi
  193. esac
  194.  
  195. # Use TZ specified by ChangeLog local variable, if any.
  196. if test -s "$changelog"
  197. then
  198.     extractTZ='
  199.         /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*"\([^"]*\)".*/{
  200.             s//\1/; p; q
  201.         }
  202.         /^.*change-log-time-zone-rule['"$tab"' ]*:['"$tab"' ]*t.*/{
  203.             s//UTC0/; p; q
  204.         }
  205.     '
  206.     logTZ=`tail "$changelog" | sed -n "$extractTZ"`
  207.     case $logTZ in
  208.     ?*) TZ=$logTZ; export TZ
  209.     esac
  210. fi
  211.  
  212. # If CVS is in use, examine its repository, not the normal RCS files.
  213. if test ! -f CVS/Repository
  214. then
  215.     rlog=rlog
  216.     repository=
  217. else
  218.     rlog='cvs -q log'
  219.     repository=`sed 1q <CVS/Repository` || exit
  220.     test ! -f CVS/Root || CVSROOT=`cat <CVS/Root` || exit
  221.     case $CVSROOT in
  222.     *:/*)
  223.         # remote repository
  224.         ;;
  225.     *)
  226.         # local repository
  227.         case $repository in
  228.         /*) ;;
  229.         *) repository=${CVSROOT?}/$repository
  230.         esac
  231.         if test ! -d "$repository"
  232.         then
  233.             echo >&2 "$0: $repository: bad repository (see CVS/Repository)"
  234.             exit 1
  235.         fi
  236.     esac
  237. fi
  238.  
  239. # Use $rlog's -zLT option, if $rlog supports it.
  240. case `$rlog -zLT 2>&1` in
  241. *' option'*) ;;
  242. *)
  243.     case $rlog_options in
  244.     '') rlog_options=-zLT;;
  245.     ?*) rlog_options=-zLT$nl$rlog_options
  246.     esac
  247. esac
  248.  
  249. # With no arguments, examine all files under the RCS directory.
  250. case $# in
  251. 0)
  252.     case $repository in
  253.     '')
  254.         oldIFS=$IFS
  255.         IFS=$nl
  256.         case $recursive in
  257.         t)
  258.             RCSdirs=`find . -name RCS -type d -print`
  259.             filesFromRCSfiles='s|,v$||; s|/RCS/|/|; s|^\./||'
  260.             files=`
  261.                 {
  262.                     case $RCSdirs in
  263.                     ?*) find $RCSdirs \
  264.                             -type f \
  265.                             ! -name '*_' \
  266.                             ! -name ',*,' \
  267.                             ! -name '.*_' \
  268.                             ! -name .rcsfreeze.log \
  269.                             ! -name .rcsfreeze.ver \
  270.                             -print
  271.                     esac
  272.                     find . -name '*,v' -print
  273.                 } |
  274.                 sort -u |
  275.                 sed "$filesFromRCSfiles"
  276.             `;;
  277.         *)
  278.             files=
  279.             for file in RCS/.* RCS/* .*,v *,v
  280.             do
  281.                 case $file in
  282.                 RCS/. | RCS/.. | RCS/,*, | RCS/*_) continue;;
  283.                 RCS/.rcsfreeze.log | RCS/.rcsfreeze.ver) continue;;
  284.                 RCS/.\* | RCS/\* | .\*,v | \*,v) test -f "$file" || continue;;
  285.                 RCS/*,v | RCS/.*,v) ;;
  286.                 RCS/* | RCS/.*) test -f "$file" || continue
  287.                 esac
  288.                 case $files in
  289.                 '') files=$file;;
  290.                 ?*) files=$files$nl$file
  291.                 esac
  292.             done
  293.             case $files in
  294.             '') exit 0
  295.             esac
  296.         esac
  297.         set x $files
  298.         shift
  299.         IFS=$oldIFS
  300.     esac
  301. esac
  302.  
  303. llogout=$TMPDIR/rcs2log$$l
  304. rlogout=$TMPDIR/rcs2log$$r
  305. trap exit 1 2 13 15
  306. trap "rm -f $llogout $rlogout; exit 1" 0
  307.  
  308. case $datearg in
  309. ?*) $rlog $rlog_options "$datearg" ${1+"$@"} >$rlogout;;
  310. '') $rlog $rlog_options ${1+"$@"} >$rlogout
  311. esac || exit
  312.  
  313.  
  314. # Get the full name of each author the logs mention, and set initialize_fullname
  315. # to awk code that initializes the `fullname' awk associative array.
  316. # Warning: foreign authors (i.e. not known in the passwd file) are mishandled;
  317. # you have to fix the resulting output by hand.
  318.  
  319. initialize_fullname=
  320. initialize_mailaddr=
  321.  
  322. case $loginFullnameMailaddrs in
  323. ?*)
  324.     case $loginFullnameMailaddrs in
  325.     *\"* | *\\*)
  326.         sed 's/["\\]/\\&/g' >$llogout <<EOF || exit
  327. $loginFullnameMailaddrs
  328. EOF
  329.         loginFullnameMailaddrs=`cat $llogout`
  330.     esac
  331.  
  332.     oldIFS=$IFS
  333.     IFS=$nl
  334.     for loginFullnameMailaddr in $loginFullnameMailaddrs
  335.     do
  336.         case $loginFullnameMailaddr in
  337.         *"$tab"*) IFS=$tab;;
  338.         *) IFS=:
  339.         esac
  340.         set x $loginFullnameMailaddr
  341.         login=$2
  342.         fullname=$3
  343.         mailaddr=$4
  344.         initialize_fullname="$initialize_fullname
  345.             fullname[\"$login\"] = \"$fullname\""
  346.         initialize_mailaddr="$initialize_mailaddr
  347.             mailaddr[\"$login\"] = \"$mailaddr\""
  348.     done
  349.     IFS=$oldIFS
  350. esac
  351.  
  352. case $llogout in
  353. ?*) sort -u -o $llogout <<EOF || exit
  354. $logins
  355. EOF
  356. esac
  357. output_authors='/^date: / {
  358.     if ($2 ~ /^[0-9]*[-\/][0-9][0-9][-\/][0-9][0-9]$/ && $3 ~ /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9][-+0-9:]*;$/ && $4 == "author:" && $5 ~ /^[^;]*;$/) {
  359.         print substr($5, 1, length($5)-1)
  360.     }
  361. }'
  362. authors=`
  363.     $AWK "$output_authors" <$rlogout |
  364.     case $llogout in
  365.     '') sort -u;;
  366.     ?*) sort -u | comm -23 - $llogout
  367.     esac
  368. `
  369. case $authors in
  370. ?*)
  371.     cat >$llogout <<EOF || exit
  372. $authors
  373. EOF
  374.     initialize_author_script='s/["\\]/\\&/g; s/.*/author[\"&\"] = 1/'
  375.     initialize_author=`sed -e "$initialize_author_script" <$llogout`
  376.     awkscript='
  377.         BEGIN {
  378.             alphabet = "abcdefghijklmnopqrstuvwxyz"
  379.             ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  380.             '"$initialize_author"'
  381.         }
  382.         {
  383.             if (author[$1]) {
  384.                 fullname = $5
  385.                 if (fullname ~ /[0-9]+-[^(]*\([0-9]+\)$/) {
  386.                     # Remove the junk from fullnames like "0000-Admin(0000)".
  387.                     fullname = substr(fullname, index(fullname, "-") + 1)
  388.                     fullname = substr(fullname, 1, index(fullname, "(") - 1)
  389.                 }
  390.                 if (fullname ~ /,[^ ]/) {
  391.                     # Some sites put comma-separated junk after the fullname.
  392.                     # Remove it, but leave "Bill Gates, Jr" alone.
  393.                     fullname = substr(fullname, 1, index(fullname, ",") - 1)
  394.                 }
  395.                 abbr = index(fullname, "&")
  396.                 if (abbr) {
  397.                     a = substr($1, 1, 1)
  398.                     A = a
  399.                     i = index(alphabet, a)
  400.                     if (i) A = substr(ALPHABET, i, 1)
  401.                     fullname = substr(fullname, 1, abbr-1) A substr($1, 2) substr(fullname, abbr+1)
  402.                 }
  403.  
  404.                 # Quote quotes and backslashes properly in full names.
  405.                 # Do not use gsub; traditional awk lacks it.
  406.                 quoted = ""
  407.                 rest = fullname
  408.                 for (;;) {
  409.                     p = index(rest, "\\")
  410.                     q = index(rest, "\"")
  411.                     if (p) {
  412.                         if (q && q<p) p = q
  413.                     } else {
  414.                         if (!q) break
  415.                         p = q
  416.                     }
  417.                     quoted = quoted substr(rest, 1, p-1) "\\" substr(rest, p, 1)
  418.                     rest = substr(rest, p+1)
  419.                 }
  420.  
  421.                 printf "fullname[\"%s\"] = \"%s%s\"\n", $1, quoted, rest
  422.                 author[$1] = 0
  423.             }
  424.         }
  425.     '
  426.  
  427.     initialize_fullname=`
  428.         {
  429.             (getent passwd $authors) ||
  430.             (
  431.                 cat /etc/passwd
  432.                 for author in $authors
  433.                 do NIS_PATH= nismatch $author passwd.org_dir
  434.                 done
  435.                 ypmatch $authors passwd
  436.             )
  437.         } 2>/dev/null |
  438.         $AWK -F: "$awkscript"
  439.     `$initialize_fullname
  440. esac
  441.  
  442.  
  443. # Function to print a single log line.
  444. # We don't use awk functions, to stay compatible with old awk versions.
  445. # `Log' is the log message (with \n replaced by \001).
  446. # `files' contains the affected files.
  447. printlogline='{
  448.  
  449.     # Following the GNU coding standards, rewrite
  450.     #    * file: (function): comment
  451.     # to
  452.     #    * file (function): comment
  453.     if (Log ~ /^\([^)]*\): /) {
  454.         i = index(Log, ")")
  455.         files = files " " substr(Log, 1, i)
  456.         Log = substr(Log, i+3)
  457.     }
  458.  
  459.     # If "label: comment" is too long, break the line after the ":".
  460.     sep = " "
  461.     if ('"$length"' <= '"$indent"' + 1 + length(files) + index(Log, SOH)) sep = "\n" indent_string
  462.  
  463.     # Print the label.
  464.     printf "%s*%s:", indent_string, files
  465.  
  466.     # Print each line of the log, transliterating \001 to \n.
  467.     while ((i = index(Log, SOH)) != 0) {
  468.         logline = substr(Log, 1, i-1)
  469.         if (logline ~ /[^'"$tab"' ]/) {
  470.             printf "%s%s\n", sep, logline
  471.         } else {
  472.             print ""
  473.         }
  474.         sep = indent_string
  475.         Log = substr(Log, i+1)
  476.     }
  477. }'
  478.  
  479. # Pattern to match the `revision' line of rlog output.
  480. rlog_revision_pattern='^revision [0-9]+\.[0-9]+(\.[0-9]+\.[0-9]+)*(['"$tab"' ]+locked by: [^'"$tab"' $,.0-9:;@]*[^'"$tab"' $,:;@][^'"$tab"' $,.0-9:;@]*;)?['"$tab"' ]*$'
  481.  
  482. case $hostname in
  483. '')
  484.     hostname=`(
  485.         hostname || uname -n || uuname -l || cat /etc/whoami
  486.     ) 2>/dev/null` || {
  487.         echo >&2 "$0: cannot deduce hostname"
  488.         exit 1
  489.     }
  490.  
  491.     case $hostname in
  492.     *.*) ;;
  493.     *)
  494.         domainname=`(domainname) 2>/dev/null` &&
  495.         case $domainname in
  496.         *.*) hostname=$hostname.$domainname
  497.         esac
  498.     esac
  499. esac
  500.  
  501.  
  502. # Process the rlog output, generating ChangeLog style entries.
  503.  
  504. # First, reformat the rlog output so that each line contains one log entry.
  505. # Transliterate \n to \001 so that multiline entries fit on a single line.
  506. # Discard irrelevant rlog output.
  507. $AWK <$rlogout '
  508.     BEGIN { repository = "'"$repository"'" }
  509.     /^RCS file:/ {
  510.         if (repository != "") {
  511.             filename = $3
  512.             if (substr(filename, 1, length(repository) + 1) == repository "/") {
  513.                 filename = substr(filename, length(repository) + 2)
  514.             }
  515.             if (filename ~ /,v$/) {
  516.                 filename = substr(filename, 1, length(filename) - 2)
  517.             }
  518.             if (filename ~ /(^|\/)Attic\/[^\/]*$/) {
  519.                 i = length(filename)
  520.                 while (substr(filename, i, 1) != "/") i--
  521.                 filename = substr(filename, 1, i - 6) substr(filename, i + 1)
  522.             }
  523.         }
  524.         rev = "?"
  525.     }
  526.     /^Working file:/ { if (repository == "") filename = $3 }
  527.     /'"$rlog_revision_pattern"'/, /^(-----------*|===========*)$/ {
  528.         line = $0
  529.         if (line ~ /'"$rlog_revision_pattern"'/) {
  530.             rev = $2
  531.             next
  532.         }
  533.         if (line ~ /^date: [0-9][- +\/0-9:]*;/) {
  534.             date = $2
  535.             if (date ~ /\//) {
  536.                 # This is a traditional RCS format date YYYY/MM/DD.
  537.                 # Replace "/"s with "-"s to get ISO format.
  538.                 newdate = ""
  539.                 while ((i = index(date, "/")) != 0) {
  540.                     newdate = newdate substr(date, 1, i-1) "-"
  541.                     date = substr(date, i+1)
  542.                 }
  543.                 date = newdate date
  544.             }
  545.             time = substr($3, 1, length($3) - 1)
  546.             author = substr($5, 1, length($5)-1)
  547.             printf "%s %s %s %s %s %c", filename, rev, date, time, author, 1
  548.             rev = "?"
  549.             next
  550.         }
  551.         if (line ~ /^branches: /) { next }
  552.         if (line ~ /^(-----------*|===========*)$/) { print ""; next }
  553.         if (line == "Initial revision" || line ~ /^file .+ was initially added on branch .+\.$/) {
  554.             line = "New file."
  555.         }
  556.         printf "%s%c", line, 1
  557.     }
  558. ' |
  559.  
  560. # Now each line is of the form
  561. # FILENAME REVISION YYYY-MM-DD HH:MM:SS[+-TIMEZONE] AUTHOR \001LOG
  562. #    where \001 stands for a carriage return,
  563. #    and each line of the log is terminated by \001 instead of \n.
  564. # Sort the log entries, first by date+time (in reverse order),
  565. # then by author, then by log entry, and finally by file name and revision
  566. # (just in case).
  567. sort +2 -4r +4 +0 |
  568.  
  569. # Finally, reformat the sorted log entries.
  570. $AWK '
  571.     BEGIN {
  572.         logTZ = "'"$logTZ"'"
  573.         revision = "'"$revision"'"
  574.  
  575.         # Some awk variants do not understand "\001", so we have to
  576.         # put the char directly in the file.
  577.         SOH="" # <-- There is a single SOH (octal code 001) here.
  578.  
  579.         # Initialize the fullname and mailaddr associative arrays.
  580.         '"$initialize_fullname"'
  581.         '"$initialize_mailaddr"'
  582.  
  583.         # Initialize indent string.
  584.         indent_string = ""
  585.         i = '"$indent"'
  586.         if (0 < '"$tabwidth"')
  587.             for (;  '"$tabwidth"' <= i;  i -= '"$tabwidth"')
  588.                 indent_string = indent_string "\t"
  589.         while (1 <= i--)
  590.             indent_string = indent_string " "
  591.     }
  592.  
  593.     {
  594.         newlog = substr($0, 1 + index($0, SOH))
  595.  
  596.         # Ignore log entries prefixed by "#".
  597.         if (newlog ~ /^#/) { next }
  598.  
  599.         if (Log != newlog || date != $3 || author != $5) {
  600.  
  601.             # The previous log and this log differ.
  602.  
  603.             # Print the old log.
  604.             if (date != "") '"$printlogline"'
  605.  
  606.             # Logs that begin with "{clumpname} " should be grouped together,
  607.             # and the clumpname should be removed.
  608.             # Extract the new clumpname from the log header,
  609.             # and use it to decide whether to output a blank line.
  610.             newclumpname = ""
  611.             sep = "\n"
  612.             if (date == "") sep = ""
  613.             if (newlog ~ /^\{[^'"$tab"' }]*}['"$tab"' ]/) {
  614.                 i = index(newlog, "}")
  615.                 newclumpname = substr(newlog, 1, i)
  616.                 while (substr(newlog, i+1) ~ /^['"$tab"' ]/) i++
  617.                 newlog = substr(newlog, i+1)
  618.                 if (clumpname == newclumpname) sep = ""
  619.             }
  620.             printf sep
  621.             clumpname = newclumpname
  622.  
  623.             # Get ready for the next log.
  624.             Log = newlog
  625.             if (files != "")
  626.                 for (i in filesknown)
  627.                     filesknown[i] = 0
  628.             files = ""
  629.         }
  630.         if (date != $3  ||  author != $5) {
  631.             # The previous date+author and this date+author differ.
  632.             # Print the new one.
  633.             date = $3
  634.             time = $4
  635.             author = $5
  636.  
  637.             zone = ""
  638.             if (logTZ && ((i = index(time, "-")) || (i = index(time, "+"))))
  639.                 zone = " " substr(time, i)
  640.  
  641.             # Print "date[ timezone]  fullname  <email address>".
  642.             # Get fullname and email address from associative arrays;
  643.             # default to author and author@hostname if not in arrays.
  644.             if (fullname[author])
  645.                 auth = fullname[author]
  646.             else
  647.                 auth = author
  648.             printf "%s%s  %s  ", date, zone, auth
  649.             if (mailaddr[author])
  650.                 printf "<%s>\n\n", mailaddr[author]
  651.             else
  652.                 printf "<%s@%s>\n\n", author, "'"$hostname"'"
  653.         }
  654.         if (! filesknown[$1]) {
  655.             filesknown[$1] = 1
  656.             if (files == "") files = " " $1
  657.             else files = files ", " $1
  658.             if (revision && $2 != "?") files = files " " $2
  659.         }
  660.     }
  661.     END {
  662.         # Print the last log.
  663.         if (date != "") {
  664.             '"$printlogline"'
  665.             printf "\n"
  666.         }
  667.     }
  668. ' &&
  669.  
  670.  
  671. # Exit successfully.
  672.  
  673. exec rm -f $llogout $rlogout
  674.  
  675. # Local Variables:
  676. # tab-width:4
  677. # End:
  678.